﻿using System;
using System.ComponentModel;

namespace SyringeControl.WinForms
{
    /// <summary>
    /// Interface of property objects notifying about value change.
    /// Allows to reduce code doubling when implementing <see cref="INotifyPropertyChanged"/> interface.
    /// </summary>
    /// <typeparam name="T">type of value stored in property</typeparam>
    public interface INotifyingProperty<T>
    {
        /// <summary>
        /// Sets property value.
        /// Raises <see cref="INotifyingProperty{T}.Changed"/>.
        /// </summary>
        /// <param name="value">value to be stored in property</param>
        void Set(T value);
        /// <summary>
        /// Returns current value of property.
        /// </summary>
        /// <returns>value of property</returns>
        T Get();
        /// <summary>
        /// Is raised when property value is changed.
        /// Argument corresponds to property name.
        /// </summary>
        event Action<string> Changed;
    }

    /// <summary>
    /// Base class for implementing <see cref="INotifyingProperty{T}"/>.
    /// </summary>
    /// <typeparam name="T">type of value stored in property</typeparam>
    public abstract class PropertyBase<T> : INotifyingProperty<T>
    {
        /// <summary>
        /// Name of property.
        /// If instance of this class is exposed via getter/setter pair, this name should correspond to owner class property name.
        /// </summary>
        public string Name { get; private set; }
        /// <summary>
        /// Is raised when property value is changed.
        /// Argument corresponds to property name.
        /// </summary>
        public event Action<string> Changed;
        /// <summary>
        /// field storing actual value of property.
        /// </summary>
        protected T innerValue;

        /// <summary>
        /// Initializes part of fields of <see cref="PropertyBase{T}"/> class.
        /// Derived classes should call this constructor.
        /// </summary>
        /// <param name="propertyName">name of property that will be passed to <see cref="PropertyBase{T}.Changed"/> handlers.</param>
        protected PropertyBase(string propertyName)
        {
            Name = propertyName;
        }
        /// <summary>
        /// Returns current value of property.
        /// </summary>
        /// <returns>value of property</returns>
        public virtual T Get() 
        {
            return innerValue;
        }
        /// <summary>
        /// Sets property value.
        /// Raises <see cref="PropertyBase{T}.Changed"/>.
        /// </summary>
        /// <param name="value">value to be stored in property</param>
        public virtual void Set(T value)
        {
            innerValue = value;
            InvokeChanged();
        }
        /// <summary>
        /// Raises <see cref="PropertyBase{T}.Changed"/> event.
        /// </summary>
        protected void InvokeChanged()
        {
            var handler = Changed;
            if(handler != null)
                handler(Name);
        }
    }


}

